package cn.llyong.rpc.client.proxy;

import cn.llyong.rpc.client.annotation.RpcReference;
import cn.llyong.rpc.client.netty.RpcClient;
import cn.llyong.rpc.common.bo.RpcRequest;
import cn.llyong.rpc.common.bo.RpcResponse;
import cn.llyong.rpc.common.enumtype.RpcResponseCode;
import cn.llyong.rpc.common.exception.RpcException;
import cn.llyong.rpc.common.utils.SpringContextUtil;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;

/**
 * @description: 动态代理类
 * @author: lvyong
 * @date: 2020年06月28日 11:27
 */
@SuppressWarnings("unchecked")
public class RpcInvoker implements InvocationHandler {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Class<?> clazz = method.getDeclaringClass();
        RpcReference rpcReference = clazz.getAnnotation(RpcReference.class);
        String appName = rpcReference.appName();
        RpcRequest request = new RpcRequest();
        request.setRequestId(UUID.randomUUID().toString().replace("-", ""));
        request.setAppName(appName);
        request.setServiceItf(clazz.getName());
        request.setMethodName(method.getName());
        Class<?>[] methodParameterTypes = method.getParameterTypes();
        if (methodParameterTypes != null && methodParameterTypes.length > 0) {
            List<RpcRequest.Param> parameterTypes = new LinkedList<>();
            RpcRequest.Param param;
            RpcRequest.Param p;
            for (int i = 0; i < methodParameterTypes.length; i++) {
                param = new RpcRequest.Param();
                param.setType(methodParameterTypes[i].getName());
                if (args != null && args.length > 0) {
                    if (args[i] == null) {
                        param.setData("");
                    } else {
                        p = new RpcRequest.Param();
                        p.setType(args[i].getClass().getName());
                        p.setData(JSON.toJSONString(args[i]));
                        param.setData(JSON.toJSONString(p));
                    }
                }
                parameterTypes.add(param);
            }
            request.setData(parameterTypes);
        }

        RpcClient rpcClient = SpringContextUtil.getBean(RpcClient.class);
        RpcResponse response = rpcClient.execute(request);
        if (RpcResponseCode.CODE_200.getCode() != response.getCode()) {
            throw new RpcException("RPC调用执行异常，" + response.getMsg());
        }
        RpcResponse.Result results = response.getData();
        if (results == null) {
            return null;
        }
        Class<?> returnType = method.getReturnType();
        if ("void".equals(returnType.getName())) {
            return null;
        }
        if (!returnType.getName().equals(results.getType())) {
            throw new RpcException("rpc服务调用返回类型不匹配！！！");
        }
        Object object = JSON.parseObject(results.getData(), returnType);
        return object;
    }

    /**
     *
     * @param clazz cglib代理/ jdk代理
     * @param <T>
     * @return
     */
    public <T> T newProxyInstance(Class<T> clazz) {
        logger.info("execute jdk dynamic proxy : {}", clazz.getSimpleName());
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
    }

}
